package cn.iocoder.yudao.module.member.service.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.CheckUtils;
import cn.iocoder.yudao.framework.common.util.CollectionUtil;
import cn.iocoder.yudao.module.member.controller.admin.images.vo.ImagesExportReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.*;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.member.convert.images.ImagesConvert;
import cn.iocoder.yudao.module.member.convert.user.CrmMemberUserConvert;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.images.ImagesDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserGasAddressDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserPageDO;
import cn.iocoder.yudao.module.member.dal.dataobject.userrelation.UserRelationDO;
import cn.iocoder.yudao.module.member.dal.mysql.user.CrmMemberUserMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
import cn.iocoder.yudao.module.member.service.images.ImagesService;
import cn.iocoder.yudao.module.member.service.userrelation.UserRelationService;
import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 会员 User Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Valid
@Slf4j
public class MemberUserServiceImpl implements MemberUserService {

    @Resource
    private MemberUserMapper memberUserMapper;

    @Resource
    private SmsCodeApi smsCodeApi;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private MemberUserProducer memberUserProducer;
    @Resource
    private UserRelationService userRelationService;
    @Resource
    private CrmMemberUserService crmMemberUserService;
    @Resource
    private ImagesService imagesService;
    @Resource
    private PayWalletService payWalletService;

    @Resource
    private CrmMemberUserMapper crmMemberUserMapper;

    @Override
    public MemberUserDO getUserByMobile(String mobile) {
        return memberUserMapper.selectByMobile(mobile);
    }

    @Override
    public List<MemberUserDO> getUserListByNickname(String nickname) {
        return memberUserMapper.selectListByNicknameLike(nickname);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MemberUserDO createUserIfAbsent(String mobile, String registerIp, Integer terminal) {
        // 用户已经存在
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user != null) {
            return user;
        }
        // 用户不存在，则进行创建
        return createUser(mobile, registerIp, terminal);
    }




    @Override
    @Transactional(rollbackFor = Exception.class)
    public MemberUserDO createMemberUserIfAbsent(MemberUserRespVO createReqVO) {
        // 用户已经存在
        MemberUserDO user = memberUserMapper.selectByMobile(createReqVO.getMobile());
        if (user != null) {
            return user;
        }
        // 用户不存在，则进行创建
        return createMemberUser(createReqVO);
    }


    /**
     * 创建客户
     * */
    @Override
    public MemberUserDO createMemberUser(MemberUserRespVO createReqVO) {
        if(CheckUtils.isEmpty(createReqVO.getPassword())){
            String password = IdUtil.fastSimpleUUID();
            createReqVO.setPassword(password);
        }
        // 插入用户
        MemberUserDO user = MemberUserConvert.INSTANCE.convert(createReqVO);
        memberUserMapper.insert(user);
        final Long userId = user.getId();
        payWalletService.getOrCreateWallet(userId, UserTypeEnum.MEMBER.getValue());
        /*// 发送 MQ 消息：用户创建
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            @Override
            public void afterCommit() {
                memberUserProducer.sendUserCreateMessage(userId);
            }

        });*/
        return user;
    }


    private MemberUserDO createUser(String mobile, String registerIp, Integer terminal) {
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        // 插入用户
        MemberUserDO user = new MemberUserDO();
        user.setMobile(mobile);
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword(password)); // 加密密码
        user.setRegisterIp(registerIp);
        user.setRegisterTerminal(terminal);
        memberUserMapper.insert(user);

        // 发送 MQ 消息：用户创建
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            @Override
            public void afterCommit() {
                memberUserProducer.sendUserCreateMessage(user.getId());
            }

        });
        return user;
    }

    @Override
    public void updateUserLogin(Long id, String loginIp) {
        memberUserMapper.updateById(new MemberUserDO().setId(id)
                .setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
    }

    @Override
    public MemberUserDO getUser(Long id) {
        return memberUserMapper.selectById(id);
    }
    @Override
    public MemberUserRespVO getMemberUser(Long id) {
        MemberUserDO memberUserDO = memberUserMapper.selectById(id);
        if (null == memberUserDO){
            return null;
        }
        List<UserRelationDO> relationDOList = userRelationService.getUserRelationList(id);
        List<CrmMemberUserRespVO> crmMemberUserRespVOList = new ArrayList<>();
        if(!CollectionUtil.isEmpty(relationDOList)){
            for(UserRelationDO urd : relationDOList){
                MemberUserGasAddressDO memberUserGasAddressDO = crmMemberUserService.getMemberUser(urd.getCrmMemberUserId());
                if(CheckUtils.isEmpty(memberUserGasAddressDO)){
                    continue;
                }
                CrmMemberUserRespVO crmMemberUserRespVO = CrmMemberUserConvert.INSTANCE.convert(memberUserGasAddressDO);
                ImagesExportReqVO imagesExportReqVO = new ImagesExportReqVO();
                imagesExportReqVO.setUserId(memberUserGasAddressDO.getId());
                imagesExportReqVO.setStatus(0);
                List<ImagesDO> imagesDOList = imagesService.getImagesList(imagesExportReqVO);
                if(!CollectionUtil.isEmpty(imagesDOList)){
                    List<ImageUserVO> imageUserVOList = ImagesConvert.INSTANCE.convertList3(imagesDOList);
                    crmMemberUserRespVO.setImageUserVOS(imageUserVOList);
                }
                crmMemberUserRespVO.setRelation(urd.getRelation());
                crmMemberUserRespVOList.add(crmMemberUserRespVO);
            }
        }
        MemberUserRespVO result = MemberUserConvert.INSTANCE.convert03(memberUserDO);
        result.setCrmMemberUserRespVOList(crmMemberUserRespVOList);
        return result;
    }

    @Override
    public List<String> getDates(String beginTime,String endTime){
        MemberUserPageReqVO memberUserPageReqVO = new MemberUserPageReqVO();
        memberUserPageReqVO.setBeginTime(beginTime);
        memberUserPageReqVO.setEndTime(endTime);
        return memberUserMapper.selectDateByMonth(memberUserPageReqVO);
    }

    @Override
    public List<MemberUserDO> getUserList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ListUtil.empty();
        }
        return memberUserMapper.selectBatchIds(ids);
    }

    @Override
    public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {
        memberUserMapper.updateById(new MemberUserDO().setId(userId)
                .setNickname(reqVO.getNickname()).setAvatar(reqVO.getAvatar()));
    }

    @Override
    public void deleteUser(Long userId) {
        memberUserMapper.updateById(new MemberUserDO().setId(userId)
                .setStatus(1));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
        // 检测用户是否存在
        MemberUserDO user = validateUserExists(userId);
        // 校验新手机是否已经被绑定
        validateMobileUnique(null, reqVO.getMobile());

        // 校验旧手机和旧验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
        // 使用新验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));

        // 更新用户手机
        memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
    }

    @Override
    public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
        // 检测用户是否存在
        MemberUserDO user = validateUserExists(userId);
        // 校验验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP()));

        // 更新用户密码
        memberUserMapper.updateById(MemberUserDO.builder().id(userId)
                .password(passwordEncoder.encode(reqVO.getPassword())).build());
    }

    @Override
    public void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO) {
        // 检验用户是否存在
        MemberUserDO user = validateUserExists(reqVO.getMobile());

        // 使用验证码
        smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_RESET_PASSWORD,
                getClientIP()));

        // 更新密码
        memberUserMapper.updateById(MemberUserDO.builder().id(user.getId())
                .password(passwordEncoder.encode(reqVO.getPassword())).build());
    }

    private MemberUserDO validateUserExists(String mobile) {
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user == null) {
            throw exception(USER_MOBILE_NOT_EXISTS);
        }
        return user;
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(MemberUserUpdateReqVO updateReqVO) {
        // 校验存在
        validateUserExists(updateReqVO.getId());
        // 校验手机唯一
        validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());

        // 更新
        MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
        memberUserMapper.updateById(updateObj);
    }

    @VisibleForTesting
    MemberUserDO validateUserExists(Long id) {
        if (id == null) {
            return null;
        }
        MemberUserDO user = memberUserMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

    @VisibleForTesting
    void validateMobileUnique(Long id, String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_MOBILE_USED, mobile);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_MOBILE_USED, mobile);
        }
    }

    @Override
    public MemberInfoCountVO getUserInfoCount(CrmMemberUserExportReqVO pageReqVO) {
        MemberInfoCountVO memberInfoCountVO = new MemberInfoCountVO();
        List<MemberUserGasAddressDO> userGasAddressDOS = crmMemberUserMapper.selectList(pageReqVO);
        // 燃气户数量
        memberInfoCountVO.setCrmMemberCount(Long.valueOf(userGasAddressDOS.size()));
        // 燃气户 民
        memberInfoCountVO.setPersonCount(userGasAddressDOS.stream().filter(x -> 0 == x.getAccountType()).count());
        // 燃气户 商业
        memberInfoCountVO.setBusinessCount(userGasAddressDOS.stream().filter(x -> 1 == x.getAccountType()).count());
        // 燃气户 工业
        memberInfoCountVO.setIndustryCount(userGasAddressDOS.stream().filter(x -> 2 == x.getAccountType()).count());
        // 燃气户 商服
        memberInfoCountVO.setShopCount(userGasAddressDOS.stream().filter(x -> 3 == x.getAccountType()).count());
        return memberInfoCountVO;
    }

    @Override
    public PageResult<MemberUserPageResultRespVO> getUserPage(MemberUserPageReqVO pageReqVO) {
        List<MemberUserPageDO> list = memberUserMapper.selectMemberList(pageReqVO);
        List<MemberUserPageResultRespVO> pageResultRespVOList = new ArrayList<>();
        if(CollectionUtil.isEmpty(list)){
            return new PageResult<>(pageResultRespVOList, 0L);
        }
        List<MemberUserPageResultRespVO> resultList = new ArrayList<>();
        for(MemberUserPageDO memberUserPageDO : list){
            MemberUserPageResultRespVO memberUserPageResultRespVO = composeMemberUserPageResultRespVO(memberUserPageDO);
            resultList.add(memberUserPageResultRespVO);
        }
        return new PageResult<MemberUserPageResultRespVO>(resultList,(long)(resultList.size()));
    }

    private MemberUserPageResultRespVO composeMemberUserPageResultRespVO(MemberUserPageDO memberUserPageDO){
        Long count = userRelationService.getUserRelationCount(memberUserPageDO.getId());
        MemberUserPageResultRespVO memberUserPageResultRespVO = MemberUserConvert.INSTANCE.convert3(memberUserPageDO);
        memberUserPageResultRespVO.setCrmCount(count);
        memberUserPageResultRespVO.setName(memberUserPageDO.getNickname());
        return memberUserPageResultRespVO;
    }

    @Override
    public void updateUserLevel(Long id, Long levelId, Integer experience) {
        // 0 代表无等级：防止UpdateById时，会被过滤掉的问题
        levelId = ObjectUtil.defaultIfNull(levelId, 0L);
        memberUserMapper.updateById(new MemberUserDO()
                .setId(id)
                .setLevelId(levelId).setExperience(experience)
        );
    }

    @Override
    public Long getUserCountByGroupId(Long groupId) {
        return memberUserMapper.selectCountByGroupId(groupId);
    }

    @Override
    public Long getUserCountByLevelId(Long levelId) {
        return memberUserMapper.selectCountByLevelId(levelId);
    }

    @Override
    public Long getUserCountByTagId(Long tagId) {
        return memberUserMapper.selectCountByTagId(tagId);
    }

    @Override
    public boolean updateUserPoint(Long id, Integer point) {
        if (point > 0) {
            memberUserMapper.updatePointIncr(id, point);
        } else if (point < 0) {
            return memberUserMapper.updatePointDecr(id, point) > 0;
        }
        return true;
    }

}
